/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2012  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.measuring.calldep;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.sosy_lab.ccvisu.InformationCollector;
import org.sosy_lab.ccvisu.Options;
import org.sosy_lab.ccvisu.Options.OptionsEnum;
import org.sosy_lab.ccvisu.Options.Verbosity;
import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.GraphEdge;
import org.sosy_lab.ccvisu.graph.GraphVertex;
import org.sosy_lab.ccvisu.graph.NameVisibility;
import org.sosy_lab.ccvisu.measuring.calldep.colors.ColorProvider;
import org.sosy_lab.ccvisu.measuring.calldep.colors.VertexColoring;
import org.sosy_lab.util.ProgressingBase;
import org.sosy_lab.util.interfaces.Progressing;

public class DependencyCalculator extends ProgressingBase implements Runnable, Progressing {

  private GraphData graph;
  private Options   options;

  public DependencyCalculator(GraphData graph, Options options) {
    this.graph = graph;
    this.options = options;
  }

  public void calculateAndSetDegree() {
    if (graph.getEdges().isEmpty()) {
      if (options.verbosity.isAtLeast(Verbosity.WARNING)) {
        System.err.println("Warning: Cannot perform measurement analysis on "
            + "empty graph.");
      }

      return;
    }

    List<VertexContainer> vertexList = getListOfVertexContainers();

    // normalize
    Collections.sort(vertexList);
    double max = vertexList.get(vertexList.size() - 1).getVertexMeasure();
    double min = vertexList.get(0).getVertexMeasure();
    double sum = 0;

    VertexColoring coloring = ColorProvider.getInstance().getColoringObject(
        options.getOption(OptionsEnum.dependencyColoring).getString(), min, max);

    for (VertexContainer vertexContainer : vertexList) {
      GraphVertex vertex = vertexContainer.getVertex();

      vertex.setColor(coloring.getColor(vertexContainer.getVertexMeasure()));
      vertex.setTooltip(vertexContainer.getMeasureName() + ": "
                        + String.valueOf(vertexContainer.getVertexMeasure()));
      sum += vertexContainer.getVertexMeasure();

      // disable showing of labels from previous runs of the algorithm
      vertex.unsetShowName(NameVisibility.Priority.DEPDEGREE);
    }

    // show labels
    int numberOfShownLabels = options.getOption(OptionsEnum.dependencyShowLabels).getInt();
    Collections.reverse(vertexList);
    Iterator<VertexContainer> iterator = vertexList.iterator();
    for (int i = 0; i < numberOfShownLabels && iterator.hasNext(); i++) {
      iterator.next().getVertex().setShowName(NameVisibility.Priority.DEPDEGREE, true);
    }

    InformationCollector infos = options.infoCollector;
    infos.addMessage("");
    infos.addMessage(String.format("Overall dependency degree: %.2f", sum));
    infos.addMessage(String.format("Minimum dependency: %.2f", min));
    infos.addMessage(String.format("Maximum dependency: %.2f", max));

    setProgress(0, 0, "Analysis finished.");
    graph.notifyAboutLayoutChange(new EventObject(this));
  }

  public List<VertexContainer> getListOfVertexContainers() {
    graph.getVertices();
    List<GraphEdge> edges = graph.getEdges();

    Map<GraphVertex, VertexContainer> vertices =
        new HashMap<GraphVertex, VertexContainer>(graph.getVertices().size());

    String algorithmName = options.getOption(OptionsEnum.dependencyAlgorithm).getString();
    for (GraphVertex vertex : graph.getVertices()) {
      if (algorithmName.equals(OutdegreeVertexContainer.ALGORITHM_NAME)) {
        vertices.put(vertex, new OutdegreeVertexContainer(vertex));
      } else if (algorithmName.equals(DistanceVertexContainer.ALGORITHM_NAME)) {
        vertices.put(vertex, new DistanceVertexContainer(vertex));
      } else { // default
        vertices.put(vertex, new DistanceVertexContainer(vertex));
      }
    }

    for (GraphEdge edge : edges) {
      vertices.get(edge.getSource()).addEdge(edge);
    }

    return new ArrayList<VertexContainer>(vertices.values());
  }

  @Override
  public void run() {
    calculateAndSetDegree();
  }
}
